其實礙於篇幅,還是很多東西想講,只好大雜燴講一下。
可能有人以為使用了 git 就是版本管理,也有人認為套件發版是一個版本,在拆分的時候才有版本管理的議題,等等原因。但我並不是這樣想,就算你不是採用微前端或是套件分包都應該要有版本管理的概念。我的觀點是:「所有的 Commit 都是獨立的版本」。
這就可以說到,我認為「只要共用就要管理版本」。共用這件事對於大部分工程師已經是再稀鬆平常的事,但隨著前端團隊的協作和專案規模的成長,「共用」帶來的卻是更大的負擔。經常我們軟體開發帶來許多問題,改 A 壞 B、套件相容、理解共用、依賴管理... 諸多問題。如果平常執行模組共用時就有明確的版本管理觀念,關注向下相容的異動設計,確保重構的影響範圍與測試
的覆蓋範圍,如此才能有效的控制共用帶來的副作用。
前面一直沒有提到測試,微前端的測試看似是一個很大的議題,但其實沒有想像中複雜與可怕。與大部分測試一樣,單元測試或整合測試之下,跨服務時就 Mock API 回傳的資料,這樣就可以完成測試。以邏輯上來說,Mock 微前端的 API 比起 Mock 後端 API 相對簡單很多,大部分情境是不需要設計太複雜的資料結構的。
我實際在實戰上遇到最多的是 React 為主,Vue 為輔的微前端開發,在一年半左右的開發中累積各種經驗。這篇要分享我在實戰開發遇到各種費盡心思處理的坑,幫助使用的人好好避開這些雷到崩潰的道路。
你如果要滿足一個完整的生命週期,你可能會遇到 Attempted to synchronously unmount a root while React was already rendering.
這個錯誤。官方的 Issue 在這,這問題是因為實際你在 React 在 Rerender 階段執行 root.unmount()
這個行為。
解決方式很簡單,你可以使用 Promise.resolve()
延遲 EventLoop 執行的時間。
這可能有多種可能,我也不保證你會遇到跟我一樣的問題,但我列出幾個可能。
如果使用 MF,它會優先使用「最高版本」。也就是說,當你先載入 v1.0.0
版本,但接著卻載入 v1.0.1
版,那 MF 會把 v1.0.1
覆蓋 v1.0.0
的版本。所以確保載入時應該要一致的版本。
以 React 來說,除了你會共享 react
和 react-dom
兩個套件。但其實我們在寫 React 時是有使用分包的,這時你必須把對應版本的分包也加入到你共享的範圍。分別是 react/jsx-runtime
和 react-dom/client
兩個入口,對於 MF 它會認為是不同的東西。
通常我們剛開始學習使用 MF 會使用 library.type=var
這個打包模式,它會直接把 container 掛載到 window 之下。比較有嘗試精神的,會嘗試處理,希望初始化不要載入那麼多資源,你可能會想用 dynamic 的方式直接使用。
const CONTAINER_NAME = "CONTAINER_NAME";
const MODULE_NAME = "./MODULE_NAME";
const container = window[CONTAINER_NAME];
const getModules = await container.get(MODULE_NAME);
const module = await getModules();
但直接這麼做會有的問題是你沒有指定作用域,所以載入後是完全不共享的。所以要程式中定義作用域,透過 webpack 在打包過程中提供的隱藏方法來取得模組。
const CONTAINER_NAME = "CONTAINER_NAME";
const MODULE_NAME = "./MODULE_NAME";
const SHARED_SCOPE = "default";
await __webpack_init_sharing__(SHARED_SCOPE);
const container = window[CONTAINER_NAME];
await container.init(__webpack_share_scopes__[SHARED_SCOPE]);
const getModules = await container.get(MODULE_NAME);
const module = await getModules();